TCA で依存性を逆転させる

モジュールが分かれてたほうが DIP のメリットを最大限受けれる (ビルド時間等) ので、マルチモジュール前提。

iso-words の実装を見ればわかる

ApiClient module と ApiClientLive module を見るのが一番分かりやすい: Package.swift
ApiClient がインターフェイスになっていて、 ApiClientLive にて ApiClient を extension して liveValue を実装している。

また、モジュール間の依存関係から見ても DIP できていて、以下の図のようになっている。

flowchart TD
  xcode[isowords.xcodeproj] --> AppFeature
  xcode --> ApiClientLive
	subgraph Swift Package
    subgraph Core library
      AppFeature --> ApiClient
  		AppFeature --> OtherFeatures
      OtherFeatures --> ApiClient
    end
  	subgraph Live library
      direction BT
  	  ApiClientLive -- add liveValue --> ApiClient
  	end
  end

swift-dependencies の実装から考える

Dependency として登録するには、 DependencyValues の subscript で取ってこれるようにする必要があった。

DependencyValues.swift

public struct DependencyValues: Sendable {
  public subscript<Key: TestDependencyKey>(
    key: Key.Type,
    file: StaticString = #file,
    function: StaticString = #function,
    line: UInt = #line
  ) -> Key.Value where Key.Value: Sendable { ... }
}

DependencyValues の subscript としての制約は TestDependencyKey であるというのが肝。
TestDependencyKeyDependencyKey の定義を見る。

public protocol TestDependencyKey {
  associatedtype Value: Sendable = Self
  static var previewValue: Value { get }
  static var testValue: Value { get }
}

public protocol DependencyKey: TestDependencyKey {
  static var liveValue: Value { get }

  associatedtype Value = Self
  static var previewValue: Value { get }
  static var testValue: Value { get }
}

つまり以下の流れで分離できるというのが、仕組みからも分かる。

依存性逆転の法則 iOS Development TCA